/*
 *
 *  Copyright (C) 2010 Tal Eisenberg
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package tal.drivechain.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.AbstractButton;
import javax.swing.DefaultButtonModel;
import javax.swing.JToggleButton.ToggleButtonModel;

/**
 *
 * @author Main
 */
public abstract class GUIButton extends AbstractButton implements MouseListener, FocusListener, KeyListener {
    private Color color;
    private Color darker;
    private boolean negative = false;
    private boolean toggle = false;
    private boolean border = false;
    private Color xor;

    public GUIButton(Color color) {
        if (toggle)
            this.setModel(new ToggleButtonModel());
        else this.setModel(new DefaultButtonModel());
        
        this.addMouseListener(this);
        this.setFocusable(true);
        this.addFocusListener(this);
        this.addKeyListener(this);
        if (color!=null) {
            this.color = color;
            darker = color.darker();
            xor = new Color(color.getRed()^255, color.getGreen()^255, color.getBlue()^255);
        }
    }

    @Override
    public void addNotify() {
        super.addNotify();
        Dimension s = calculateSize();
        setPreferredSize(s);
        setMaximumSize(s);
        setMinimumSize(s);
        setSize(s);
    }

    public void setHasBorder(boolean hasBorder) { this.border = hasBorder; }
    public boolean hasBorder() { return border; }

    public void setNegative(boolean negative) { 
        this.negative = negative;
    }


    public void setToggleButton(boolean toggle) {
        this.toggle = toggle;
        if (toggle)
            this.setModel(new ToggleButtonModel());
        else this.setModel(new DefaultButtonModel());
        this.repaint();
    }

    public boolean isToggleButton() {
        return toggle;
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (model.isArmed())
            paintArmed(g);
        else if (model.isRollover())
            paintHover(g);
        else 
            paintReleased(g);

        if (this.hasFocus())
            paintFocus(g);
    }

    public void setColor(Color c) {
        if (c==null) return;
        color = c;
        darker = c.darker();
        xor = new Color(color.getRed()^255, color.getGreen()^255, color.getBlue()^255);
        repaint();
    }

    protected abstract void drawImage(Graphics g);
    protected abstract Dimension calculateSize();

    private void paintBackground(Graphics g) {
        g.fillRect(0,0,getWidth(),getHeight());
    }

    private void paintButtonBorder(Graphics g) {
        g.drawRect(0,0,getWidth()-1, getHeight()-1);
    }

    private void paintFocus(Graphics g) {
        if (!negative)
            g.setColor(Color.BLACK);
        else g.setColor(color);
        g.drawLine(2, getHeight()-2, getWidth()-2, getHeight()-2);
    }

    private void paintHover(Graphics g) {
        if (negative) {
            // if has border: bright border, black background else: dark background anyway: color text
            if (toggle && isSelected()) {
                g.setColor(darker);
                paintBackground(g);
                g.setColor(Color.BLACK);
                drawImage(g);
            } else {
                g.setColor(Color.BLACK);
                paintBackground(g);
                g.setColor(color);
                drawImage(g);
            }
            if (border)
                g.setColor(xor);
            else g.setColor(color);
            paintButtonBorder(g);
        } else {
            // if has border: dark border anyway: black background, color image

            if (toggle && isSelected()) {
                g.setColor(darker);
                paintBackground(g);
                g.setColor(xor);
                drawImage(g);
                if (border) {
                    g.setColor(color);
                    paintButtonBorder(g);
                }
            } else {
                g.setColor(color);
                paintBackground(g);
                if (toggle)
                    g.setColor(Color.BLACK);
                else g.setColor(xor);
                drawImage(g);
                if (border) {
                    g.setColor(darker);
                    paintButtonBorder(g);
                }
            }
        }
    }

    private void paintArmed(Graphics g) {
        if (negative) {
            g.setColor(color);
            paintBackground(g);
            g.setColor(Color.BLACK);
            drawImage(g);
            if (border)
                g.setColor(xor);
            else g.setColor(color);
            paintButtonBorder(g);
        } else {
            g.setColor(Color.BLACK);
            paintBackground(g);
            g.setColor(darker);
            drawImage(g);
            if (border)
                g.setColor(darker);
            else g.setColor(color);
            paintButtonBorder(g);
        }
    }

    private void paintReleased(Graphics g) {
        if (negative) {
            if (toggle && isSelected()) {
                g.setColor(darker);
                paintBackground(g);
                g.setColor(Color.BLACK);
                drawImage(g);
            } else {
                g.setColor(Color.BLACK);
                paintBackground(g);
                g.setColor(color);
                drawImage(g);
            }
            if (border) {
                g.setColor(darker);
                paintButtonBorder(g);
            }
        } else { // !negative
            if (toggle && isSelected()) {
                g.setColor(color);
                paintBackground(g);
                g.setColor(Color.BLACK);
                drawImage(g);
            } else {
                if (toggle) {
                    g.setColor(darker);
                }  else
                    g.setColor(color);
                paintBackground(g);
                if (toggle) {
                    g.setColor(color);
                    g.drawRect(0, 0, getWidth()-1, getHeight()-1);
                }

                g.setColor(Color.BLACK);
                drawImage(g);
            }

            g.setColor(darker);
            if (border)
                paintButtonBorder(g);
        }
    }

    public void mouseEntered(MouseEvent e) {
        model.setRollover(true);
        repaint();
    }

    public void mousePressed(MouseEvent e) {
        model.setArmed(true);
        repaint();
    }

    public void mouseReleased(MouseEvent e) {
        model.setArmed(false);
        repaint();
    }

    public void mouseExited(MouseEvent e) {
        model.setArmed(false);
        model.setRollover(false);
        repaint();
    }

    public void mouseClicked(MouseEvent e) {
        if (e.getButton()==MouseEvent.BUTTON1) {
            model.setArmed(false);
            repaint();
            actionClick();
            model.setRollover(false);
            repaint();
        }
    }

    public void keyTyped(KeyEvent e) {
    }


    public void keyReleased(KeyEvent e) {
    }

    public void keyPressed(KeyEvent e) {
        if (this.hasFocus() && e.getKeyCode()==KeyEvent.VK_SPACE)
            actionClick();
    }

    private void actionClick() {
        if (!this.isEnabled()) return;
        
        setSelected(!isSelected());
        for (ActionListener l : this.getActionListeners()) {
            l.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "click"));
        }
    }
    
    public void focusGained(FocusEvent e) {
        repaint();
    }

    public void focusLost(FocusEvent e) {
        repaint();
    }

}